iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
自我挑戰組

前端工程師的java學習紀錄系列 第 29

Day29-錯誤、異常

  • 分享至 

  • xImage
  •  

異常指的會是在程式碼執行期間,發生了不如預期 的錯誤,而不是單純的邏輯 上的錯誤,例如:

  • 異常:當要使用陣列的索引 進行操作時,誤將索引 寫成超出 陣列長度的情況,這就會出現ArrayIndexOfBoundsException(陣列索引超出範圍異常) 的狀況。
  • 非異常:要求a + b 的和,誤寫成a / b,這種屬於邏輯錯誤而非異常

當發生異常而未進行處理時,JVM會因為錯誤而停止運作,Java中異常super class 是對應到java.lang.Throwable,裡面又可分為兩個:

  • Error:Java虛擬機無法處理的嚴重問題,一般不會寫針對性的程式碼進行處理這類型的問題(因為JVM會停止,只能針對程式碼進行修改)。例如:JVM系統內部錯誤、資源耗盡等的嚴重狀況。
  • Exception:在寫程式碼時有錯誤,或者是使用者操作發生偶然的錯誤。例如:ArrayIndexOfBoundsException(陣列索引超出範圍異常) 、讀取不存在的文件等。

Exception中又可分為兩種狀況:

  • 編譯時 (受檢異常)出現異常
  • 運行時 (非受檢異常)出現異常

常見運行時的異常:(以下程式碼省略最外層的部分,只寫核心的內容)

  1. ArrayIndexOfBoundsException
int[] arr = new int[10];
System.out.println(arr[10]); //ArrayIndexOfBoundsException 
  1. NullPointerException``空指針異常物件還沒有實例化僅有記憶體中的位址值,這時去查詢物件內的資料。
int[] arr = new int[10];
int = null;
System.out.println(arr[0]); //NullPointerException 
  1. ClassCastException:當進行強制轉型時,型別不符合。
String str = "hello";
Data date = (Date) str; //ClassCastException
  1. NumberFormatException:數字轉換不符合類型。
String str = "123";
int i = Integer.parseInt(str); // 可以
str = "123a";
int i1 = Integer.parseInt(str); //NumberFormatException
  1. InputMismatchException:輸入的類型不符合對應類型。
Scanner scan = new Scanner(System.in); //位在java.util底下的一個類,可以獲取使用者鍵盤輸入的內容
int i = scan.nextInt(); //如果使用者輸入數字可以將其賦值給變數
// 假設使用者輸入abc
System.out.println(i); //InputMismatchException
  1. ArithmeticException:算數異常。
int i = 10;
System.out.println(i / 0); //ArithmeticException

編譯時異常(必須在程式碼中另外處理)

可以使用try{} catch(Type err){} finally{} 的格式對可能出現異常的程式碼進行處理。

  • try:裡面呈現可能會出現異常的程式碼,當發生異常時,發生異常後的程式碼皆不會直接(直接跳入catch 比對異常類型)。
  • catch:可以有多個catch,每個catch會有一個相對應的異常類型,當出現異常時,會依次進行異常類型 的比對,當比對到相同的異常類型時,則會忽略其他的catch
    💡寫多個catch時,在catch 參數中的異常類型存在繼承 關係時,需要先從sub class開始寫。
  • finally:裡面放的是不論是否有異常都會執行的程式碼。
public class ExceptionHandleTest {
	public static void main(String[] args) {
		try {
			int i = 10;
			System.out.println(i / 0);
		} catch(ArithmeticException e) {
			System.out.println("The wrong is being handled");
		}
			System.out.println("Hello");
		
	}
}
The wrong is being handled

Hello

當有使用catch將錯誤抓住的話,下面的Hello就可以正常被執行出來,但是像上方的這種異常因為是屬於程式碼本身就有瑕疵,所以需要去修改程式碼。

使用finally

public class ExceptionHandleTest {
	public static void main(String[] args) {
		try {
			int i = 10;
			System.out.println(i / 0);
		} catch(ArithmeticException e) {
			System.out.println("The wrong is being handled");
			System.out.println(10 / 0);
		} finally {
			System.out.println("Finally");
		}
			System.out.println("Hello");
		
	}
}
The wrong is being handled

Finally

假設catch中也存在其他的異常時,Hello就不會被執行了,但是在finally中的程式碼是不論怎麼樣都會執行(即使在trycatch中使用return )。

在實際開發中,有些資源的操作必須要手動的進行,否則GC不會主動的將它們回收,所以會將這些操作放在finally中。例如:資料庫連接、Socket等等。


使用throws異常的方式,如下method()僅是將異常向上丟給了使用它的方法method1(),但是在method1()中,還是需要去處理這個異常的問題。


public void method1() {
	try {
		method();
	} catch(FileNotFoundException err) {
		err.printStackTrace(); // 此方法會在控制台中印出完整的錯誤訊息
	}
}

public void method() throws FileNotFoundException {
	
}

前面在重寫Override時有講過,sub class重寫方法時,若是有throws異常,異常的類型必須跟super class相同或是該異常類型的sub class,這是由於多態的關係。

在實作接口時,若是接口的抽象方法沒有throws則實作的方法也不能使用throws

interface Flyable {
	public void fly() throws Exception;
}

class Plane implements Flyable {
	@Override
	public void fly() throws Exception {
	}
}

異常處理的選擇try-catch-finally or throws ?

try-catch-finally

  • 當涉及到資源的使用(如資料庫連接、Socket等等)。
  • super class繼承的方法沒有寫throws

throws

  • 當方法是有連貫性的情況,假設方法是由a()b()c()依序連貫進行時,在a()b()c()三個方法都會使用throws的方式,並且在實際使用這三個方法的位置進行try-catch-finally的處理。因為假設錯誤是發生在a(),當直接在a()中進行處理後,會繼續往b()執行,但是b()是依賴於a()的正常執行,這時又會接續b()c()的錯誤,所以會統一在同時使用a()b()c()的方法中一次處理。

throw:可以透過使用throw new (異常類型物件)的方式,手動將一個異常丟出如下:

public class ThrowTest {
	public static void main(String[] args) {
		Student student = new Student();
		try {
			student.register(-5);
			System.out.println(student.id);
		} catch(Exception err) {
			err.printStackTrace(); // 輸入id非法
		}
	}
}

class Student {
	int id;
	
	public void register(int id) throws Exception {
		if(id > 0) {
			this.id = id;
		} else {
			throw new Exception("輸入id非法");
		}
		
	}
}

自定義異常類(可以參考jave.lang中異常類的寫法)。

  1. 必須繼承於現有的異常類,通常會繼承RuntimeExceptionException兩種。
  2. 通常會提供幾個overload的構造器。
  3. 提供一個全局常量,需定義為:static final long serialVersionUID;

使用自定義異常類最主要的重點在於能夠透過這個異常類的名稱,就知道具體發生的異常狀況是甚麼。


上一篇
Day28-註解Annotation、包裝類
下一篇
Day30-中場結尾,開始了就要有好結局?
系列文
前端工程師的java學習紀錄38
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言